/* 
 * Enterprise Library Extensions - DI + ORM.
 * Copyright (C) 2006
 *
 * GNU General Public License
 *
 * This program is free software; you can redistribute 
 * it and/or modify it under the terms of the GNU General Public License 
 * as published by the Free Software Foundation; either version 2 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
 * See the GNU General Public License for more details.
 */
using System;
using System.Collections.Generic;
using System.Text;

namespace ELE.EnterpriseLibrary.Data
{
  /// <summary>
  /// A class used to manage objects stored in a relational database, defined by the O/R Application Block.
  /// 
  /// <para>Following is an example of using this class:</para>
  /// <code>
  ///  Microsoft.Patterns.EnterpriseLibrary.Data.Database db = 
  ///          Microsoft.Patterns.EnterpriseLibrary.Data.DatabaseFactory.CreateDatabase();
  /// 
  ///  ELE.EnterpriseLibrary.Data.ObjectStorage store = 
  ///         ELE.EnterpriseLibrary.Data.ObjectStorageFactory.CreateStorage();
  ///  IObjectQueryRunner runner = store.GetQueryRunner();
  /// 
  ///  Employee inputObject = new Employee();
  ///  inputObject.Id = 0;
  /// 
  ///  // Run a query by its id and return a DataSet
  ///  System.Data.DataSet ds = runner.ExecuteDatasetQueryByName(db, "GetEmployeeById", inputObject);
  /// 
  ///  // Run a query by its id and return an IList of Employee objects
  ///  System.Collections.IList objects = runner.ExecuteQueryByName(db, "GetEmployeeById", inputObject);
  /// 
  ///  // Run a query by its id and return an IList of Employee objects but do not recurse the bindings
  ///  IList lightObjects = runner.ExecuteQueryByNameLightweight(db, "GetEmployeeById", inputObject);
  /// 
  ///  // Run a query by using the primary key defined in the mapping configuration of the identified object
  ///  Employee employee = (Employee)runner.QueryObjectByPrimaryKey(db, "Employee", inputObject.Id);
  /// 
  ///  // Run a query by using the primary key defined in the mapping configuration of the identified object
  ///  // Do not recurse the bindings.
  ///  Employee lightEmployee = (Employee)runner.QueryObjectByPrimaryKeyLightweight(db, "Employee", inputObject.Id);
  /// 
  ///  // Assume that the BuildEmployee() method simply constructs an Employee object with the proper set of data
  ///  Employee upEmployee = this.BuildEmployee();
  /// 
  ///  // Persist the employee object in the database. This will do an insert if the id of the employee is new
  ///  // to the database or it will do an update if it is already in the database.
  ///  int addedRows = runner.PersistObject(db, "Employee", upEmployee);
  ///  
  ///  // Assume that the AddEquipmentToEmployee method adds an equipment object to the employee object.
  ///  this.AddEquipmentToEmployee(upEmployee, 4, "0334442332");
  ///
  ///  // Persist the employee object. This will update all tables that relate to the employee object and 
  ///  // all mapped objects contained within it, including an insert of the equipment just added
  ///  // above to the proper mapped table for the Equipment object.
  ///  int updatedRows = runner.PersistObject(db, "Employee", upEmployee);
  /// 
  ///  // Delete the employee from the database, including all mapped objects contained within it.
  ///  int deletedRows = runner.RemoveObject(db, "Employee", upEmployee);
  /// </code>
  /// <para>The above code assumes that you have a configuration file that contains the proper configuration data
  /// that conforms to the <see cref="ELE.EnterpriseLibrary.Data.Configuration.ObjectStorageSettings"/> requirements. Furthermore, since the factory
  /// method without parameters is used, this file is actually the App.config or Web.config of the application 
  /// where this code resides.</para>
  /// <para>Notice the dependency on the <see cref="Microsoft.Practices.EnterpriseLibrary.Data.Database"/> object
  /// which suggest that the proper configuration settings also exist in the application's configuration file
  /// for the EL's Data Access Application Block</para>
  /// </summary>
  public class ObjectStorage : NamedItem, ICloneable
  {
    private Dictionary<string, ObjectSqlMappingSet> mappings;
    private Dictionary<string, ObjectSqlQuery> queries;
    private Dictionary<string, ObjectStoredProcedure> storedProcedures;
    private string defaultQuery;
    private string defaultMapping;
    /// <summary>
    /// Named constructor
    /// </summary>
    /// <param name="name"></param>
    public ObjectStorage(string name) : base(name) { }
    /// <summary>
    /// Gets a generic query runner (<see cref="ObjectQueryManager"/>).
    /// </summary>
    /// <returns></returns>
    public virtual IObjectQueryRunner GetQueryRunner()
    {
      return new ObjectQueryManager(this);
    }
    /// <summary>
    /// Get a query by its name.
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    public ObjectSqlQuery GetQuery(string name)
    {
      if (queries.ContainsKey(name))
      {
        return queries[name];
      }
      return null;
    }
    /// <summary>
    /// Get a stored procedure by its name.
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    public ObjectStoredProcedure GetStoredProcedure(string name)
    {
      if (storedProcedures.ContainsKey(name))
      {
        return storedProcedures[name];
      }
      return null;
    }
    /// <summary>
    /// Get a mapping set.
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    public ObjectSqlMappingSet GetMappingSet(string name)
    {
      if (name == null) name = this.defaultMapping;
      if (mappings.ContainsKey(name))
      {
        return mappings[name];
      }
      return null;
    }
    /// <summary>
    /// get a specific object to sql mapping.
    /// </summary>
    /// <param name="mapName"></param>
    /// <param name="objectName"></param>
    /// <returns></returns>
    public ObjectSqlMapping GetMapping(string mapName, string objectName)
    {
      ObjectSqlMappingSet set = this.GetMappingSet(mapName);
      if (set != null)
      {
        return set.GetObjectMapping(objectName);
      }
      return null;
    }
    /// <summary>
    /// The default query.
    /// </summary>
    public string DefaultQueryName
    {
      get { return defaultQuery; }
      set { defaultQuery = value; }
    }
    /// <summary>
    /// The default mapping.
    /// </summary>
    public string DefaultMappingName
    {
      get { return defaultMapping; }
      set { defaultMapping = value; }
    }
    /// <summary>
    /// Add a mapping
    /// </summary>
    /// <param name="mapping"></param>
    public void AddMapping(ObjectSqlMapping mapping)
    {
      if (this.mappings == null)
      {
        this.mappings = new Dictionary<string, ObjectSqlMappingSet>();
      }
      ObjectSqlMappingSet map = this.GetMappingSet(mapping.MapName);
      if (map == null)
      {
        map = new ObjectSqlMappingSet();
        map.AddObjectMapping(mapping);
        this.mappings.Add(mapping.MapName, map);
      }
      else
      {
        map.AddObjectMapping(mapping);
      }
    }
    /// <summary>
    /// Add a query.
    /// </summary>
    /// <param name="query"></param>
    public void AddQuery(ObjectSqlQuery query)
    {
      if (this.queries == null)
      {
        this.queries = new Dictionary<string, ObjectSqlQuery>();
      }
      this.queries.Add(query.Name, query);
    }

    /// <summary>
    /// Add a stored procedure.
    /// </summary>
    /// <param name="procedure"></param>
    public void AddStoredProcedure(ObjectStoredProcedure procedure)
    {
      if (this.storedProcedures == null)
      {
        this.storedProcedures = new Dictionary<string, ObjectStoredProcedure>();
      }
      this.storedProcedures.Add(procedure.Name, procedure);
    }

    #region ICloneable Members
    /// <summary>
    /// Implementation of <see cref="ICloneable.Clone()"/>
    /// </summary>
    /// <returns></returns>
    public object Clone()
    {
      ObjectStorage store = new ObjectStorage(this.Name);
      if (this.mappings != null)
      {
        foreach (ObjectSqlMappingSet map in this.mappings.Values)
        {
          foreach (ObjectSqlMapping mapping in map.Mappings.Values)
          {
            store.AddMapping((ObjectSqlMapping)mapping.Clone());
          }
        }
      }
      if (this.queries != null)
      {
        foreach (ObjectSqlQuery query in this.queries.Values)
        {
          store.AddQuery((ObjectSqlQuery)query.Clone());
        }
      }
      if (this.storedProcedures != null)
      {
        foreach (ObjectStoredProcedure procedure in this.storedProcedures.Values)
        {
          store.AddStoredProcedure((ObjectStoredProcedure)procedure.Clone());
        }
      }
      return store;
    }

    #endregion
  }
}
